package com.log4jviewer.ui.preferences;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.IntegerFieldEditor;
import org.eclipse.jface.preference.StringFieldEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.log4jviewer.Activator;

/**
 * Class represents main preference page with general settings for server, log file and LogView.
 * 
 * @author <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a>
 */
public class PreferencePage extends FieldEditorPreferencePage implements
        IWorkbenchPreferencePage {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static Set<PreferencePageListener> listeners = new HashSet<PreferencePageListener>();

    private IPreferenceStore store;

    private ServerSettingsComposer serverSettings;

    @Override
    public void init(final IWorkbench workbench) {
        store = Activator.getInstance().getPreferenceStore();
        setPreferenceStore(store);
        setDescription("General settings for logviewer.");
    }

    @Override
    protected void createFieldEditors() {
        GridLayout gridLayout = new GridLayout();
        gridLayout.verticalSpacing = 15;
        Composite composite = getFieldEditorParent();
        composite.setLayout(gridLayout);

        // Server settings
        serverSettings = new ServerSettingsComposer(composite);
        logger.debug("Server settings were composed");

        // Log File settings
        composeLogFileSettings(composite);

        // Log view settings
        composeLogViewSettings(composite);
    }

    private void composeLogFileSettings(final Composite composite) {
        Group logFileSettings = buildSettingsGroup(composite, "Log File settings");
        GridData gridData = (GridData) logFileSettings.getLayoutData();
        gridData.heightHint = 35;

        StringFieldEditor layoutPatternField = new StringFieldEditor(DefaultPreferences.LogFilePreferences.getId(),
                " &Layout pattern:", logFileSettings);
        layoutPatternField.getTextControl(logFileSettings).setLayoutData(
                new GridData(SWT.FILL, SWT.CENTER, true, false));
        addField(layoutPatternField);

        logger.debug("LogFile settings were composed");
    }

    private void composeLogViewSettings(final Composite composite) {
        Group logViewSettings = buildSettingsGroup(composite, "LogView output limitation");
        GridData gridData = (GridData) logViewSettings.getLayoutData();
        gridData.heightHint = 35;

        IntegerFieldEditor outputLimetaionField = new IntegerFieldEditor(DefaultPreferences.LogViewPreferences.getId(),
                " &LogView buffer size (logs):", logViewSettings);
        outputLimetaionField.getTextControl(logViewSettings).setLayoutData(
                new GridData(SWT.FILL, SWT.CENTER, true, false));
        addField(outputLimetaionField);

        logger.debug("LogView settings were composed");
    }

    private void saveServerSettingsAndNotifyObservers() {
        StringBuilder serverPrefs = new StringBuilder();

        String host = serverSettings.getHost();
        serverPrefs.append(host).append(ServerPreferences.getSeparator());
        logger.debug("Saving host: {}", host);

        String port = serverSettings.getPort();
        serverPrefs.append(port).append(ServerPreferences.getSeparator());
        logger.debug("Saving port: {}", port);

        boolean startup = serverSettings.isStartup();
        serverPrefs.append(startup);
        logger.debug("Saving startup: {}", startup);

        store.setValue(ServerPreferences.getId(), serverPrefs.toString());

        // Notify listeners that server settings were changed.
        notifyListeners(this);
    }

    private static Group buildSettingsGroup(final Composite composite, final String groupName) {
        Group groupSettings = new Group(composite, SWT.NONE);
        groupSettings.setText(groupName);
        GridLayout gridLayout = new GridLayout();
        gridLayout.numColumns = 2;
        groupSettings.setLayout(gridLayout);
        GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
        gridData.horizontalSpan = 2;
        groupSettings.setLayoutData(gridData);

        return groupSettings;
    }

    public static void notifyListeners(final PreferencePage event) {
        for (PreferencePageListener listener : listeners) {
            listener.handleMainPreferenceEvent(event);
        }
    }

    public static void addListener(final PreferencePageListener listener) {
        if (listener != null) {
            listeners.add(listener);
        }
    }

    public static void removeListener(final PreferencePageListener listener) {
        listeners.remove(listener);
    }

    @Override
    protected void performApply() {
        super.performApply();
        saveServerSettingsAndNotifyObservers();
    }

    @Override
    public boolean performOk() {
        boolean isSaved = super.performOk();
        saveServerSettingsAndNotifyObservers();
        return isSaved;
    }

    private static final class ServerSettingsComposer {

        private Text hostField;

        private Text portField;

        private Button startupButton;

        private ServerSettingsComposer(final Composite composite) {
            composeServerSettings(composite);
        }

        public String getHost() {
            return hostField.getText();
        }

        public String getPort() {
            return portField.getText();
        }

        public boolean isStartup() {
            return startupButton.getSelection();
        }

        private void composeServerSettings(final Composite composite) {
            ServerPreferences serverPreferences = new ServerPreferences();
            serverPreferences.loadServerPreferences();

            Group settingsGroup = buildSettingsGroup(composite, "Server settings");

            Label host = new Label(settingsGroup, SWT.BEGINNING);
            host.setText("&Host:");
            hostField = new Text(settingsGroup, SWT.BORDER);
            hostField.setText(serverPreferences.getHost());
            GridData hostFieldData = new GridData(SWT.FILL, SWT.CENTER, true, false);
            hostField.setLayoutData(hostFieldData);

            Label port = new Label(settingsGroup, SWT.BEGINNING);
            port.setText("&Port:");
            portField = new Text(settingsGroup, SWT.BORDER);
            portField.setText(String.valueOf(serverPreferences.getPort()));
            GridData portFieldData = new GridData(SWT.FILL, SWT.CENTER, true, false);
            portField.setLayoutData(portFieldData);

            Label startup = new Label(settingsGroup, SWT.BEGINNING);
            startup.setText("&Automatic start");
            startupButton = new Button(settingsGroup, SWT.CHECK);
            startupButton.setSelection(serverPreferences.isStarted());
        }
    }
}
